package com.wbc.demo;

import com.wbc.utils.ThreadTestUtil;

import java.util.HashSet;
import java.util.Set;

/**
 * ThreadLocal 线程私有控制并发
 * @author wangbingchen
 * @Description
 * @create 2021-12-10 10:01
 */
public class ThreadLocalDemo {


    private static int num = 0;

    /**
     * 仔细看运行流程
     * @param args
     */
    public static void main(String[] args) {


        //测试线线程安全运行
        runThreadSafe();

        //测试线线程不安全运行 会因为并发导致数据永远到不了 2000 而死循环
        runThreadUnsafe();





    }

    private static void runThreadSafe() {
        System.out.println("线程安全开始测试程序测试开始运行");
        System.out.println("创建 200 个线程进行模拟每个线程 10个并发,共计 2000 个并发");
        int threadNum = 200;
        int concurrentNum = 10;
        ThreadTestUtil.myThreadUtil(ThreadLocalDemo.class,"actualBusiness",threadNum,concurrentNum);

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //统计 执行结果 多线程还在执行中所以需要 循环统计 看最后多线程执行结束 看最终结果是否符合预期
        long startTime = System.currentTimeMillis();
        int integer;
        do {
            integer = getNum();

        } while (threadNum * concurrentNum != integer);
        System.out.println("检测到执行完的数据："+integer);
        System.out.println("共耗时："+(System.currentTimeMillis()-startTime)+"ms");
    }

    private static void runThreadUnsafe() {
        System.out.println("线程不安全开始测试程序测试开始运行");
        System.out.println("创建 200 个线程进行模拟每个线程 10个并发,共计 2000 个并发");
        int threadNum = 200;
        int concurrentNum = 10;
        ThreadTestUtil.myThreadUtil(ThreadLocalDemo.class,"actualBusiness1",threadNum,concurrentNum);

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //统计 执行结果 多线程还在执行中所以需要 循环统计 看最后多线程执行结束 看最终结果是否符合预期
        long startTime = System.currentTimeMillis();
        int integer;
        do {
            integer = getNum1();

        } while (threadNum * concurrentNum != integer);
        System.out.println("检测到执行完的数据："+integer);
        System.out.println("共耗时："+(System.currentTimeMillis()-startTime)+"ms");

    }


    //统计结果 线程不安全
    public static Integer getNum1(){
        return num;
    }

    //模拟业务方法 增加库存 并发执行 线程不安全
    public static void actualBusiness1() {
        System.out.println("开始执行业务。。。");
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("增加库存");
        num++;
    }




    //统计结果
    public static Integer getNum(){
        return set.stream().map(Val::getV).reduce(Integer::sum).get();
    }

    //模拟业务方法 增加库存 并发执行 线程安全
    public static void actualBusiness() {
        System.out.println("开始执行业务。。。");
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("增加库存");
        addSynchronized(1);
    }



    //新增下单数量 此方法线程安全
    private static void addSynchronized(int i) {
        Val<Integer> integerVal = x.get();
        integerVal.setV(integerVal.getV()+i);
        x.set(integerVal);
    }


    //用于存储分散在线程中的值
    private static Set<Val<Integer>> set = new HashSet<Val<Integer>>();

    //线程私有变量 不受并发影响
    private static ThreadLocal<Val<Integer>> x = new ThreadLocal<Val<Integer>>(){
        @Override
        protected Val<Integer> initialValue() {
            Val<Integer> val = new Val<Integer>();
            val.setV(0);
            addSet(val);
            return val;
        }
    };

    //控制线程初始化
    private synchronized static void addSet(Val<Integer> val) {
        set.add(val);
        System.out.println(Thread.currentThread().getId()+"线程初始化完毕！");
    }
}

class Val<T> {

    private T v;


    public T getV() {
    return v;
    }

    public void setV(T v) {
        this.v = v;
    }
}
